﻿using BJoin.Application.AppException;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

namespace BJoin.Application.WebApi.Middlewares
{
    public class HttpStatusCodeExceptionMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<HttpStatusCodeExceptionMiddleware> _logger;

        public HttpStatusCodeExceptionMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
        {
            _next = next ?? throw new ArgumentNullException(nameof(next));
            _logger = loggerFactory?.CreateLogger<HttpStatusCodeExceptionMiddleware>() ?? throw new ArgumentNullException(nameof(loggerFactory));
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (ArgumentNullException ex)
            {
                if (context.Response.HasStarted)
                {
                    _logger.LogWarning(ex, "The response has already started, the http status code Handler will not be executed.");
                }
                _logger.LogWarning(ex, $"the request throw argument null exception with message {ex.Message}");
                context.Response.Clear();
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                context.Response.ContentType = "text/html;charset=utf-8";
                await context.Response.WriteAsync($"argument null:{ex.Message}");
            }
            catch (ArgumentException ex)
            {
                if (context.Response.HasStarted)
                {
                    _logger.LogWarning("The response has already started, the http status code Handler will not be executed.");
                    throw;
                }
                _logger.LogWarning($"the request throw argument exception with message {ex.Message}");
                context.Response.Clear();
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                context.Response.ContentType = "text/html;charset=utf-8";
                await context.Response.WriteAsync(ex.Message);
            }
            catch (NotImplementedException ex)
            {
                if (context.Response.HasStarted)
                {
                    _logger.LogWarning("The response has already started, the http status code Handler will not be executed.");
                    throw;
                }
                _logger.LogWarning($"the request throw not implement exception with message {ex.Message}");
                context.Response.Clear();
                context.Response.StatusCode = StatusCodes.Status501NotImplemented;
                context.Response.ContentType = "text/html;charset=utf-8";
                await context.Response.WriteAsync(ex.Message);
            }
            catch (HttpStatusCodeException ex)
            {
                if (context.Response.HasStarted)
                {
                    _logger.LogWarning("The response has already started, the http status code Handler will not be executed.");
                    throw;
                }
                if (ex.StatusCode >= 400 && ex.StatusCode<500)
                    _logger.LogWarning($"the request throw expected exception with statusCode {ex.StatusCode} and message {ex.Message}");
                else if (ex.StatusCode >=500)
                    _logger.LogError(ex,$"the request throw expected exception with statusCode {ex.StatusCode} and message {ex.Message}");
                context.Response.Clear();
                context.Response.StatusCode = ex.StatusCode;
                context.Response.ContentType = ex.ContentType;
                await context.Response.WriteAsync(ex.Message);
            }
#pragma warning disable CA1031 // Do not catch general exception types
            catch (Exception ex)
            {
                _logger.LogError(ex,$"the request throw unexpected exception");
                context.Response.Clear();
                context.Response.StatusCode = StatusCodes.Status500InternalServerError;
                context.Response.ContentType = @"text/html;charset=utf-8";
                await context.Response.WriteAsync(ex.Message);
            }
#pragma warning restore CA1031 // Do not catch general exception types
        }
    }

    // Extension method used to add the Middleware to the HTTP request pipeline.
    public static class HttpStatusCodeExceptionMiddlewareExtensions
    {
        public static IApplicationBuilder UseHttpStatusCodeExceptionMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<HttpStatusCodeExceptionMiddleware>();
        }
    }
}
